﻿using HWInfo.Models;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace HWInfo.ARM
{
    internal class RK3399HWInfo : HardwareInfoBase, IHardwareInfo
    {

        public MemoryStatus MemoryStatus => GetMemoryStatus();

        public float CpuTemperature => GetCpuTemperature();

        public float CpuUsage => GetCpuUsage();

        /// <summary>
        /// 磁盘信息
        /// </summary>
        public List<Disk> HDD => GetDisks();

        public RK3399HWInfo()
        {
        }


        public string GetMachineSN(bool encrypt = true)
        {
            string result;
            var sn = GetCpuSN();

            if (sn != string.Empty)
            {
                result = sn;
            }
            else
            {
                sn = GetFirstDiskSN();
                if (sn != string.Empty)
                {
                    result = sn;
                }
                else
                {
                    result = string.Empty;
                }
            }
            if (encrypt && result != string.Empty)
            {
                using (var md5 = MD5.Create())
                {
                    var tmp = md5.ComputeHash(Encoding.UTF8.GetBytes(result));
                    result = BitConverter.ToString(tmp);
                    result = result.Replace("-", "");
                    result = Guid.Parse(result).ToString().ToUpper();
                }
                return result;
            }
            else
            {
                return result;
            }
        }

        private static string GetCpuSN()
        {
            try
            {
                Regex snRegx = new(@"Serial.*:\s+(.+)");

                var txt = TryReadFileText("/proc/cpuinfo");

                Match match = snRegx.Match(txt);
                if (match.Success && match.Groups.Count > 0)
                {
                    return "cpuid" +  match.Groups[1].Value.Replace(" ", "");
                }
                else
                {
                    return string.Empty;
                }
            }
            catch (Exception)
            {
                return string.Empty;
            }
        }

        private static string GetFirstDiskSN()
        {
            try
            {
                string result = string.Empty;
                var cmd = "lsblk --nodeps -no name,type,serial --json";

                string tmp = Execute(cmd, 0);

                JObject obj = JObject.Parse(tmp);

                JArray arr = obj["blockdevices"] as JArray;

                foreach (var item in arr)
                {
                    var name = item["name"].ToString();
                    var type = item["type"].ToString();
                    var serial = item["serial"].ToString();

                    if (name.Contains("nvme0") && type == "disk" && serial != string.Empty)
                    {
                        result += serial;
                        break;
                    }

                    if (name.Contains("nvme1") && type == "disk" && serial != string.Empty)
                    {
                        result += serial;
                        break;
                    }

                    if (name.Contains("nvme") && type == "disk" && serial != string.Empty)
                    {
                        result += serial;
                        break;
                    }

                    if (name == "sda" && type == "disk" && serial != string.Empty)
                    {
                        result += serial;
                        break;
                    }

                    if (name.Contains("mmcblk0") && type == "disk" && serial != string.Empty)
                    {
                        result += serial;
                        break;
                    }

                    if (name.Contains("mmcblk1") && type == "disk" && serial != string.Empty)
                    {
                        result += serial;
                        break;
                    }

                    if (name.Contains("mmcblk") && type == "disk" && serial != string.Empty)
                    {
                        result += serial;
                        break;
                    }

                    if (name.Contains("sr0") && type == "rom" && serial != string.Empty)
                    {
                        result += serial;
                        break;
                    }
                }
                if (!string.IsNullOrEmpty(result))
                    result = "diskid" + result;

                return result.Replace(" ", "");
            }
            catch (Exception)
            {
                return string.Empty;
            }
        }

        private static MemoryStatus GetMemoryStatus()
        {
            var ms = new MemoryStatus();
            try
            {
                
                var txt = TryReadFileText("/proc/meminfo");
                Regex MemTotalRegx = new(@"MemTotal:\s+(.+) ");
                Match match = MemTotalRegx.Match(txt);
                if (match.Success && match.Groups.Count > 0)
                {
                    ms.Total = Convert.ToDouble(match.Groups[1].Value.Trim()) / 1024;
                }
                else
                {
                    ms.Total = 0;
                }
                Regex MemAvailableRegx = new(@"MemAvailable:\s+(.+) ");
                match = MemAvailableRegx.Match(txt);
                if (match.Success && match.Groups.Count > 0)
                {
                    ms.Available = Convert.ToDouble(match.Groups[1].Value.Trim()) / 1024;
                }
                else
                {
                    ms.Available = 0;
                }

                Regex MemFreeRegx = new(@"MemFree:\s+(.+) ");
                match = MemFreeRegx.Match(txt);
                if (match.Success && match.Groups.Count > 0)
                {
                    ms.Free = Convert.ToDouble(match.Groups[1].Value.Trim()) / 1024;
                }
                else
                {
                    ms.Free = 0;
                }

                Regex MemBuffersRegx = new(@"Buffers:\s+(.+) ");
                match = MemBuffersRegx.Match(txt);
                if (match.Success && match.Groups.Count > 0)
                {
                    ms.Buffers = Convert.ToDouble(match.Groups[1].Value.Trim()) / 1024;
                }
                else
                {
                    ms.Buffers = 0;
                }

                Regex MemCachedRegx = new(@"Cached:\s+(.+) ");
                match = MemCachedRegx.Match(txt);
                if (match.Success && match.Groups.Count > 0)
                {
                    ms.Cached = Convert.ToDouble(match.Groups[1].Value.Trim()) / 1024;
                }
                else
                {
                    ms.Cached = 0;
                }

                Regex SwapTotalRegx = new(@"SwapTotal:\s+(.+) ");
                match = SwapTotalRegx.Match(txt);
                if (match.Success && match.Groups.Count > 0)
                {
                    ms.SwapTotal = Convert.ToDouble(match.Groups[1].Value.Trim()) / 1024;
                }
                else
                {
                    ms.SwapTotal = 0;
                }

                Regex SwapFreeRegx = new(@"SwapFree:\s+(.+) ");
                match = SwapFreeRegx.Match(txt);
                if (match.Success && match.Groups.Count > 0)
                {
                    ms.SwapAvailable = Convert.ToDouble(match.Groups[1].Value.Trim()) / 1024;
                }
                else
                {
                    ms.SwapAvailable = 0;
                }

            }
            catch (Exception)
            {
                ms = null;
            }
            return ms;
        }

        private static float GetCpuTemperature()
        {
            try
            {
                var cpu1 = TryReadFileText("/sys/class/thermal/thermal_zone0/temp");
                var cpu2 = TryReadFileText("/sys/class/thermal/thermal_zone1/temp");

                var num = 0;
                var t1 = int.Parse(cpu1) < 0 ? 0 : int.Parse(cpu1);
                if (t1 > 0)
                    num++;

                var t2 = int.Parse(cpu2) < 0 ? 0 : int.Parse(cpu2);
                if (t2 > 0)
                    num++;


                var tmp = (t1 + t2) / 1000.0f / num;

                return tmp;
            }
            catch (Exception)
            {
                return 0.0f;
            }
        }

        private static float GetCpuUsage()
        {
            try
            {
                var cmd = "top -b -n 1 | sed -n 3p | awk '{print $2}'";
                var result = Execute(cmd, 0).Replace(Environment.NewLine, "").Split(" ", StringSplitOptions.RemoveEmptyEntries);
                return Convert.ToSingle(result[0]);
            }
            catch (Exception)
            {
                return 0.0f;
            }
        }

        public static List<Disk> GetDisks()
        {
            var disks = new List<Disk>();
            try
            {
                const string cmd = "df -m | grep -E '/dev/sd|/dev/nvme|/dev/mmc'";
                var result = Execute(cmd, 0)
                    .Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);

                foreach (var tmp in result)
                {
                    var disk = new Disk();
                    var items = tmp.Split(" ", StringSplitOptions.RemoveEmptyEntries);
                    disk.Name = items[0];
                    disk.Total = Convert.ToSingle(items[1]);
                    disk.Used = Convert.ToSingle(items[2]);
                    disk.Free = Convert.ToSingle(items[3]);
                    disk.Usage = Convert.ToSingle(items[4].Replace("%", ""));
                    disk.Directory = items[5];
                    disks.Add(disk);
                }

            }
            catch (Exception)
            {
                
            }
            return disks;
        }



        //public override List<NetworkAdapter> GetNetworkAdapterList()
        //{
        //    List<NetworkAdapter> networkAdapterList = base.GetNetworkAdapterList();

        //    char[] charSeparators = new char[] { ' ' };

        //    foreach (NetworkAdapter networkAdapter in networkAdapterList)
        //    {
        //        List<string>? networkAdapterUsageLast = TryReadFileLines("/proc/net/dev").FirstOrDefault(l => l.Trim().StartsWith(networkAdapter.Name))?.Trim().Split(charSeparators, StringSplitOptions.RemoveEmptyEntries).ToList();
        //        Task.Delay(1000).Wait();
        //        List<string>? networkAdapterUsageNow = TryReadFileLines("/proc/net/dev").FirstOrDefault(l => l.Trim().StartsWith(networkAdapter.Name))?.Trim().Split(charSeparators, StringSplitOptions.RemoveEmptyEntries).ToList();

        //        if (networkAdapterUsageLast != null && networkAdapterUsageLast.Count > 0 && networkAdapterUsageNow != null && networkAdapterUsageNow.Count > 0)
        //        {
        //            networkAdapter.BytesReceivedPersec = Convert.ToUInt64(networkAdapterUsageNow[1]) - Convert.ToUInt64(networkAdapterUsageLast[1]);
        //            networkAdapter.BytesSentPersec = Convert.ToUInt64(networkAdapterUsageNow[9]) - Convert.ToUInt64(networkAdapterUsageLast[9]);
        //        }
        //    }

        //    return networkAdapterList;
        //}

        public List<NetworkAdapter> NetworkAdapters => GetNetworkAdapterList();
    }
}
